1   /*
2    * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  /*
27   * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28   * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29   *
30   *   The original version of this source code and documentation is copyrighted
31   * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32   * materials are provided under terms of a License Agreement between Taligent
33   * and Sun. This technology is protected by multiple US and International
34   * patents. This notice and attribution to Taligent may not be removed.
35   *   Taligent is a registered trademark of Taligent, Inc.
36   *
37   */
38  
39  package java.text;
40  
41  import java.io.IOException;
42  import java.io.ObjectOutputStream;
43  import java.io.Serializable;
44  import java.lang.ref.SoftReference;
45  import java.text.spi.DateFormatSymbolsProvider;
46  import java.util.Arrays;
47  import java.util.List;
48  import java.util.Locale;
49  import java.util.ResourceBundle;
50  import java.util.TimeZone;
51  import java.util.concurrent.ConcurrentHashMap;
52  import java.util.concurrent.ConcurrentMap;
53  import java.util.spi.LocaleServiceProvider;
54  import sun.util.LocaleServiceProviderPool;
55  import sun.util.TimeZoneNameUtility;
56  import sun.util.calendar.ZoneInfo;
57  import sun.util.resources.LocaleData;
58  
59  /**
60   * <code>DateFormatSymbols</code> is a public class for encapsulating
61   * localizable date-time formatting data, such as the names of the
62   * months, the names of the days of the week, and the time zone data.
63   * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
64   * <code>DateFormatSymbols</code> to encapsulate this information.
65   *
66   * <p>
67   * Typically you shouldn't use <code>DateFormatSymbols</code> directly.
68   * Rather, you are encouraged to create a date-time formatter with the
69   * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
70   * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
71   * These methods automatically create a <code>DateFormatSymbols</code> for
72   * the formatter so that you don't have to. After the
73   * formatter is created, you may modify its format pattern using the
74   * <code>setPattern</code> method. For more information about
75   * creating formatters using <code>DateFormat</code>'s factory methods,
76   * see {@link DateFormat}.
77   *
78   * <p>
79   * If you decide to create a date-time formatter with a specific
80   * format pattern for a specific locale, you can do so with:
81   * <blockquote>
82   * <pre>
83   * new SimpleDateFormat(aPattern, DateFormatSymbols.getInstance(aLocale)).
84   * </pre>
85   * </blockquote>
86   *
87   * <p>
88   * <code>DateFormatSymbols</code> objects are cloneable. When you obtain
89   * a <code>DateFormatSymbols</code> object, feel free to modify the
90   * date-time formatting data. For instance, you can replace the localized
91   * date-time format pattern characters with the ones that you feel easy
92   * to remember. Or you can change the representative cities
93   * to your favorite ones.
94   *
95   * <p>
96   * New <code>DateFormatSymbols</code> subclasses may be added to support
97   * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
98  
99   * @see          DateFormat
100  * @see          SimpleDateFormat
101  * @see          java.util.SimpleTimeZone
102  * @author       Chen-Lieh Huang
103  */
104 public class DateFormatSymbols implements Serializable, Cloneable {
105 
106     /**
107      * Construct a DateFormatSymbols object by loading format data from
108      * resources for the default locale. This constructor can only
109      * construct instances for the locales supported by the Java
110      * runtime environment, not for those supported by installed
111      * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
112      * implementations. For full locale coverage, use the
113      * {@link #getInstance(Locale) getInstance} method.
114      *
115      * @see #getInstance()
116      * @exception  java.util.MissingResourceException
117      *             if the resources for the default locale cannot be
118      *             found or cannot be loaded.
119      */
120     public DateFormatSymbols()
121     {
122         initializeData(Locale.getDefault(Locale.Category.FORMAT));
123     }
124 
125     /**
126      * Construct a DateFormatSymbols object by loading format data from
127      * resources for the given locale. This constructor can only
128      * construct instances for the locales supported by the Java
129      * runtime environment, not for those supported by installed
130      * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
131      * implementations. For full locale coverage, use the
132      * {@link #getInstance(Locale) getInstance} method.
133      *
134      * @see #getInstance(Locale)
135      * @exception  java.util.MissingResourceException
136      *             if the resources for the specified locale cannot be
137      *             found or cannot be loaded.
138      */
139     public DateFormatSymbols(Locale locale)
140     {
141         initializeData(locale);
142     }
143 
144     /**
145      * Era strings. For example: "AD" and "BC".  An array of 2 strings,
146      * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
147      * @serial
148      */
149     String eras[] = null;
150 
151     /**
152      * Month strings. For example: "January", "February", etc.  An array
153      * of 13 strings (some calendars have 13 months), indexed by
154      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
155      * @serial
156      */
157     String months[] = null;
158 
159     /**
160      * Short month strings. For example: "Jan", "Feb", etc.  An array of
161      * 13 strings (some calendars have 13 months), indexed by
162      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
163 
164      * @serial
165      */
166     String shortMonths[] = null;
167 
168     /**
169      * Weekday strings. For example: "Sunday", "Monday", etc.  An array
170      * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
171      * <code>Calendar.MONDAY</code>, etc.
172      * The element <code>weekdays[0]</code> is ignored.
173      * @serial
174      */
175     String weekdays[] = null;
176 
177     /**
178      * Short weekday strings. For example: "Sun", "Mon", etc.  An array
179      * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
180      * <code>Calendar.MONDAY</code>, etc.
181      * The element <code>shortWeekdays[0]</code> is ignored.
182      * @serial
183      */
184     String shortWeekdays[] = null;
185 
186     /**
187      * AM and PM strings. For example: "AM" and "PM".  An array of
188      * 2 strings, indexed by <code>Calendar.AM</code> and
189      * <code>Calendar.PM</code>.
190      * @serial
191      */
192     String ampms[] = null;
193 
194     /**
195      * Localized names of time zones in this locale.  This is a
196      * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
197      * where <em>m</em> is at least 5.  Each of the <em>n</em> rows is an
198      * entry containing the localized names for a single <code>TimeZone</code>.
199      * Each such row contains (with <code>i</code> ranging from
200      * 0..<em>n</em>-1):
201      * <ul>
202      * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
203      * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
204      * time</li>
205      * <li><code>zoneStrings[i][2]</code> - short name of zone in
206      * standard time</li>
207      * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
208      * saving time</li>
209      * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
210      * saving time</li>
211      * </ul>
212      * The zone ID is <em>not</em> localized; it's one of the valid IDs of
213      * the {@link java.util.TimeZone TimeZone} class that are not
214      * <a href="../java/util/TimeZone.html#CustomID">custom IDs</a>.
215      * All other entries are localized names.
216      * @see java.util.TimeZone
217      * @serial
218      */
219     String zoneStrings[][] = null;
220 
221     /**
222      * Indicates that zoneStrings is set externally with setZoneStrings() method.
223      */
224     transient boolean isZoneStringsSet = false;
225 
226     /**
227      * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
228      * All locales use the same these unlocalized pattern characters.
229      */
230     static final String  patternChars = "GyMdkHmsSEDFwWahKzZYuX";
231 
232     static final int PATTERN_ERA                  =  0; // G
233     static final int PATTERN_YEAR                 =  1; // y
234     static final int PATTERN_MONTH                =  2; // M
235     static final int PATTERN_DAY_OF_MONTH         =  3; // d
236     static final int PATTERN_HOUR_OF_DAY1         =  4; // k
237     static final int PATTERN_HOUR_OF_DAY0         =  5; // H
238     static final int PATTERN_MINUTE               =  6; // m
239     static final int PATTERN_SECOND               =  7; // s
240     static final int PATTERN_MILLISECOND          =  8; // S
241     static final int PATTERN_DAY_OF_WEEK          =  9; // E
242     static final int PATTERN_DAY_OF_YEAR          = 10; // D
243     static final int PATTERN_DAY_OF_WEEK_IN_MONTH = 11; // F
244     static final int PATTERN_WEEK_OF_YEAR         = 12; // w
245     static final int PATTERN_WEEK_OF_MONTH        = 13; // W
246     static final int PATTERN_AM_PM                = 14; // a
247     static final int PATTERN_HOUR1                = 15; // h
248     static final int PATTERN_HOUR0                = 16; // K
249     static final int PATTERN_ZONE_NAME            = 17; // z
250     static final int PATTERN_ZONE_VALUE           = 18; // Z
251     static final int PATTERN_WEEK_YEAR            = 19; // Y
252     static final int PATTERN_ISO_DAY_OF_WEEK      = 20; // u
253     static final int PATTERN_ISO_ZONE             = 21; // X
254 
255     /**
256      * Localized date-time pattern characters. For example, a locale may
257      * wish to use 'u' rather than 'y' to represent years in its date format
258      * pattern strings.
259      * This string must be exactly 18 characters long, with the index of
260      * the characters described by <code>DateFormat.ERA_FIELD</code>,
261      * <code>DateFormat.YEAR_FIELD</code>, etc.  Thus, if the string were
262      * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
263      * @serial
264      */
265     String  localPatternChars = null;
266 
267     /**
268      * The locale which is used for initializing this DateFormatSymbols object.
269      *
270      * @since 1.6
271      * @serial
272      */
273     Locale locale = null;
274 
275     /* use serialVersionUID from JDK 1.1.4 for interoperability */
276     static final long serialVersionUID = -5987973545549424702L;
277 
278     /**
279      * Returns an array of all locales for which the
280      * <code>getInstance</code> methods of this class can return
281      * localized instances.
282      * The returned array represents the union of locales supported by the
283      * Java runtime and by installed
284      * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
285      * implementations.  It must contain at least a <code>Locale</code>
286      * instance equal to {@link java.util.Locale#US Locale.US}.
287      *
288      * @return An array of locales for which localized
289      *         <code>DateFormatSymbols</code> instances are available.
290      * @since 1.6
291      */
292     public static Locale[] getAvailableLocales() {
293         LocaleServiceProviderPool pool=
294             LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
295         return pool.getAvailableLocales();
296     }
297 
298     /**
299      * Gets the <code>DateFormatSymbols</code> instance for the default
300      * locale.  This method provides access to <code>DateFormatSymbols</code>
301      * instances for locales supported by the Java runtime itself as well
302      * as for those supported by installed
303      * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
304      * implementations.
305      * @return a <code>DateFormatSymbols</code> instance.
306      * @since 1.6
307      */
308     public static final DateFormatSymbols getInstance() {
309         return getInstance(Locale.getDefault(Locale.Category.FORMAT));
310     }
311 
312     /**
313      * Gets the <code>DateFormatSymbols</code> instance for the specified
314      * locale.  This method provides access to <code>DateFormatSymbols</code>
315      * instances for locales supported by the Java runtime itself as well
316      * as for those supported by installed
317      * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
318      * implementations.
319      * @param locale the given locale.
320      * @return a <code>DateFormatSymbols</code> instance.
321      * @exception NullPointerException if <code>locale</code> is null
322      * @since 1.6
323      */
324     public static final DateFormatSymbols getInstance(Locale locale) {
325         DateFormatSymbols dfs = getProviderInstance(locale);
326         if (dfs != null) {
327             return dfs;
328         }
329         return (DateFormatSymbols) getCachedInstance(locale).clone();
330     }
331 
332     /**
333      * Returns a DateFormatSymbols provided by a provider or found in
334      * the cache. Note that this method returns a cached instance,
335      * not its clone. Therefore, the instance should never be given to
336      * an application.
337      */
338     static final DateFormatSymbols getInstanceRef(Locale locale) {
339         DateFormatSymbols dfs = getProviderInstance(locale);
340         if (dfs != null) {
341             return dfs;
342         }
343         return getCachedInstance(locale);
344     }
345 
346     private static DateFormatSymbols getProviderInstance(Locale locale) {
347         DateFormatSymbols providersInstance = null;
348 
349         // Check whether a provider can provide an implementation that's closer
350         // to the requested locale than what the Java runtime itself can provide.
351         LocaleServiceProviderPool pool =
352             LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
353         if (pool.hasProviders()) {
354             providersInstance = pool.getLocalizedObject(
355                                     DateFormatSymbolsGetter.INSTANCE, locale);
356         }
357         return providersInstance;
358     }
359 
360     /**
361      * Returns a cached DateFormatSymbols if it's found in the
362      * cache. Otherwise, this method returns a newly cached instance
363      * for the given locale.
364      */
365     private static DateFormatSymbols getCachedInstance(Locale locale) {
366         SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
367         DateFormatSymbols dfs = null;
368         if (ref == null || (dfs = ref.get()) == null) {
369             dfs = new DateFormatSymbols(locale);
370             ref = new SoftReference<DateFormatSymbols>(dfs);
371             SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref);
372             if (x != null) {
373                 DateFormatSymbols y = x.get();
374                 if (y != null) {
375                     dfs = y;
376                 } else {
377                     // Replace the empty SoftReference with ref.
378                     cachedInstances.put(locale, ref);
379                 }
380             }
381         }
382         return dfs;
383     }
384 
385     /**
386      * Gets era strings. For example: "AD" and "BC".
387      * @return the era strings.
388      */
389     public String[] getEras() {
390         return Arrays.copyOf(eras, eras.length);
391     }
392 
393     /**
394      * Sets era strings. For example: "AD" and "BC".
395      * @param newEras the new era strings.
396      */
397     public void setEras(String[] newEras) {
398         eras = Arrays.copyOf(newEras, newEras.length);
399     }
400 
401     /**
402      * Gets month strings. For example: "January", "February", etc.
403      * @return the month strings.
404      */
405     public String[] getMonths() {
406         return Arrays.copyOf(months, months.length);
407     }
408 
409     /**
410      * Sets month strings. For example: "January", "February", etc.
411      * @param newMonths the new month strings.
412      */
413     public void setMonths(String[] newMonths) {
414         months = Arrays.copyOf(newMonths, newMonths.length);
415     }
416 
417     /**
418      * Gets short month strings. For example: "Jan", "Feb", etc.
419      * @return the short month strings.
420      */
421     public String[] getShortMonths() {
422         return Arrays.copyOf(shortMonths, shortMonths.length);
423     }
424 
425     /**
426      * Sets short month strings. For example: "Jan", "Feb", etc.
427      * @param newShortMonths the new short month strings.
428      */
429     public void setShortMonths(String[] newShortMonths) {
430         shortMonths = Arrays.copyOf(newShortMonths, newShortMonths.length);
431     }
432 
433     /**
434      * Gets weekday strings. For example: "Sunday", "Monday", etc.
435      * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
436      * <code>Calendar.MONDAY</code>, etc. to index the result array.
437      */
438     public String[] getWeekdays() {
439         return Arrays.copyOf(weekdays, weekdays.length);
440     }
441 
442     /**
443      * Sets weekday strings. For example: "Sunday", "Monday", etc.
444      * @param newWeekdays the new weekday strings. The array should
445      * be indexed by <code>Calendar.SUNDAY</code>,
446      * <code>Calendar.MONDAY</code>, etc.
447      */
448     public void setWeekdays(String[] newWeekdays) {
449         weekdays = Arrays.copyOf(newWeekdays, newWeekdays.length);
450     }
451 
452     /**
453      * Gets short weekday strings. For example: "Sun", "Mon", etc.
454      * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
455      * <code>Calendar.MONDAY</code>, etc. to index the result array.
456      */
457     public String[] getShortWeekdays() {
458         return Arrays.copyOf(shortWeekdays, shortWeekdays.length);
459     }
460 
461     /**
462      * Sets short weekday strings. For example: "Sun", "Mon", etc.
463      * @param newShortWeekdays the new short weekday strings. The array should
464      * be indexed by <code>Calendar.SUNDAY</code>,
465      * <code>Calendar.MONDAY</code>, etc.
466      */
467     public void setShortWeekdays(String[] newShortWeekdays) {
468         shortWeekdays = Arrays.copyOf(newShortWeekdays, newShortWeekdays.length);
469     }
470 
471     /**
472      * Gets ampm strings. For example: "AM" and "PM".
473      * @return the ampm strings.
474      */
475     public String[] getAmPmStrings() {
476         return Arrays.copyOf(ampms, ampms.length);
477     }
478 
479     /**
480      * Sets ampm strings. For example: "AM" and "PM".
481      * @param newAmpms the new ampm strings.
482      */
483     public void setAmPmStrings(String[] newAmpms) {
484         ampms = Arrays.copyOf(newAmpms, newAmpms.length);
485     }
486 
487     /**
488      * Gets time zone strings.  Use of this method is discouraged; use
489      * {@link java.util.TimeZone#getDisplayName() TimeZone.getDisplayName()}
490      * instead.
491      * <p>
492      * The value returned is a
493      * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
494      * where <em>m</em> is at least 5.  Each of the <em>n</em> rows is an
495      * entry containing the localized names for a single <code>TimeZone</code>.
496      * Each such row contains (with <code>i</code> ranging from
497      * 0..<em>n</em>-1):
498      * <ul>
499      * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
500      * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
501      * time</li>
502      * <li><code>zoneStrings[i][2]</code> - short name of zone in
503      * standard time</li>
504      * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
505      * saving time</li>
506      * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
507      * saving time</li>
508      * </ul>
509      * The zone ID is <em>not</em> localized; it's one of the valid IDs of
510      * the {@link java.util.TimeZone TimeZone} class that are not
511      * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
512      * All other entries are localized names.  If a zone does not implement
513      * daylight saving time, the daylight saving time names should not be used.
514      * <p>
515      * If {@link #setZoneStrings(String[][]) setZoneStrings} has been called
516      * on this <code>DateFormatSymbols</code> instance, then the strings
517      * provided by that call are returned. Otherwise, the returned array
518      * contains names provided by the Java runtime and by installed
519      * {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider}
520      * implementations.
521      *
522      * @return the time zone strings.
523      * @see #setZoneStrings(String[][])
524      */
525     public String[][] getZoneStrings() {
526         return getZoneStringsImpl(true);
527     }
528 
529     /**
530      * Sets time zone strings.  The argument must be a
531      * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
532      * where <em>m</em> is at least 5.  Each of the <em>n</em> rows is an
533      * entry containing the localized names for a single <code>TimeZone</code>.
534      * Each such row contains (with <code>i</code> ranging from
535      * 0..<em>n</em>-1):
536      * <ul>
537      * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
538      * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
539      * time</li>
540      * <li><code>zoneStrings[i][2]</code> - short name of zone in
541      * standard time</li>
542      * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
543      * saving time</li>
544      * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
545      * saving time</li>
546      * </ul>
547      * The zone ID is <em>not</em> localized; it's one of the valid IDs of
548      * the {@link java.util.TimeZone TimeZone} class that are not
549      * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
550      * All other entries are localized names.
551      *
552      * @param newZoneStrings the new time zone strings.
553      * @exception IllegalArgumentException if the length of any row in
554      *    <code>newZoneStrings</code> is less than 5
555      * @exception NullPointerException if <code>newZoneStrings</code> is null
556      * @see #getZoneStrings()
557      */
558     public void setZoneStrings(String[][] newZoneStrings) {
559         String[][] aCopy = new String[newZoneStrings.length][];
560         for (int i = 0; i < newZoneStrings.length; ++i) {
561             int len = newZoneStrings[i].length;
562             if (len < 5) {
563                 throw new IllegalArgumentException();
564             }
565             aCopy[i] = Arrays.copyOf(newZoneStrings[i], len);
566         }
567         zoneStrings = aCopy;
568         isZoneStringsSet = true;
569     }
570 
571     /**
572      * Gets localized date-time pattern characters. For example: 'u', 't', etc.
573      * @return the localized date-time pattern characters.
574      */
575     public String getLocalPatternChars() {
576         return localPatternChars;
577     }
578 
579     /**
580      * Sets localized date-time pattern characters. For example: 'u', 't', etc.
581      * @param newLocalPatternChars the new localized date-time
582      * pattern characters.
583      */
584     public void setLocalPatternChars(String newLocalPatternChars) {
585         // Call toString() to throw an NPE in case the argument is null
586         localPatternChars = newLocalPatternChars.toString();
587     }
588 
589     /**
590      * Overrides Cloneable
591      */
592     public Object clone()
593     {
594         try
595         {
596             DateFormatSymbols other = (DateFormatSymbols)super.clone();
597             copyMembers(this, other);
598             return other;
599         } catch (CloneNotSupportedException e) {
600             throw new InternalError();
601         }
602     }
603 
604     /**
605      * Override hashCode.
606      * Generates a hash code for the DateFormatSymbols object.
607      */
608     public int hashCode() {
609         int hashcode = 0;
610         String[][] zoneStrings = getZoneStringsWrapper();
611         for (int index = 0; index < zoneStrings[0].length; ++index)
612             hashcode ^= zoneStrings[0][index].hashCode();
613         return hashcode;
614     }
615 
616     /**
617      * Override equals
618      */
619     public boolean equals(Object obj)
620     {
621         if (this == obj) return true;
622         if (obj == null || getClass() != obj.getClass()) return false;
623         DateFormatSymbols that = (DateFormatSymbols) obj;
624         return (Arrays.equals(eras, that.eras)
625                 && Arrays.equals(months, that.months)
626                 && Arrays.equals(shortMonths, that.shortMonths)
627                 && Arrays.equals(weekdays, that.weekdays)
628                 && Arrays.equals(shortWeekdays, that.shortWeekdays)
629                 && Arrays.equals(ampms, that.ampms)
630                 && Arrays.deepEquals(getZoneStringsWrapper(), that.getZoneStringsWrapper())
631                 && ((localPatternChars != null
632                   && localPatternChars.equals(that.localPatternChars))
633                  || (localPatternChars == null
634                   && that.localPatternChars == null)));
635     }
636 
637     // =======================privates===============================
638 
639     /**
640      * Useful constant for defining time zone offsets.
641      */
642     static final int millisPerHour = 60*60*1000;
643 
644     /**
645      * Cache to hold DateFormatSymbols instances per Locale.
646      */
647     private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
648         = new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3);
649 
650     private void initializeData(Locale desiredLocale) {
651         locale = desiredLocale;
652 
653         // Copy values of a cached instance if any.
654         SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
655         DateFormatSymbols dfs;
656         if (ref != null && (dfs = ref.get()) != null) {
657             copyMembers(dfs, this);
658             return;
659         }
660 
661         // Initialize the fields from the ResourceBundle for locale.
662         ResourceBundle resource = LocaleData.getDateFormatData(locale);
663 
664         eras = resource.getStringArray("Eras");
665         months = resource.getStringArray("MonthNames");
666         shortMonths = resource.getStringArray("MonthAbbreviations");
667         ampms = resource.getStringArray("AmPmMarkers");
668         localPatternChars = resource.getString("DateTimePatternChars");
669 
670         // Day of week names are stored in a 1-based array.
671         weekdays = toOneBasedArray(resource.getStringArray("DayNames"));
672         shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations"));
673     }
674 
675     private static String[] toOneBasedArray(String[] src) {
676         int len = src.length;
677         String[] dst = new String[len + 1];
678         dst[0] = "";
679         for (int i = 0; i < len; i++) {
680             dst[i + 1] = src[i];
681         }
682         return dst;
683     }
684 
685     /**
686      * Package private: used by SimpleDateFormat
687      * Gets the index for the given time zone ID to obtain the time zone
688      * strings for formatting. The time zone ID is just for programmatic
689      * lookup. NOT LOCALIZED!!!
690      * @param ID the given time zone ID.
691      * @return the index of the given time zone ID.  Returns -1 if
692      * the given time zone ID can't be located in the DateFormatSymbols object.
693      * @see java.util.SimpleTimeZone
694      */
695     final int getZoneIndex(String ID)
696     {
697         String[][] zoneStrings = getZoneStringsWrapper();
698         for (int index=0; index<zoneStrings.length; index++)
699         {
700             if (ID.equals(zoneStrings[index][0])) return index;
701         }
702 
703         return -1;
704     }
705 
706     /**
707      * Wrapper method to the getZoneStrings(), which is called from inside
708      * the java.text package and not to mutate the returned arrays, so that
709      * it does not need to create a defensive copy.
710      */
711     final String[][] getZoneStringsWrapper() {
712         if (isSubclassObject()) {
713             return getZoneStrings();
714         } else {
715             return getZoneStringsImpl(false);
716         }
717     }
718 
719     private final String[][] getZoneStringsImpl(boolean needsCopy) {
720         if (zoneStrings == null) {
721             zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
722         }
723 
724         if (!needsCopy) {
725             return zoneStrings;
726         }
727 
728         int len = zoneStrings.length;
729         String[][] aCopy = new String[len][];
730         for (int i = 0; i < len; i++) {
731             aCopy[i] = Arrays.copyOf(zoneStrings[i], zoneStrings[i].length);
732         }
733         return aCopy;
734     }
735 
736     private final boolean isSubclassObject() {
737         return !getClass().getName().equals("java.text.DateFormatSymbols");
738     }
739 
740     /**
741      * Clones all the data members from the source DateFormatSymbols to
742      * the target DateFormatSymbols. This is only for subclasses.
743      * @param src the source DateFormatSymbols.
744      * @param dst the target DateFormatSymbols.
745      */
746     private final void copyMembers(DateFormatSymbols src, DateFormatSymbols dst)
747     {
748         dst.eras = Arrays.copyOf(src.eras, src.eras.length);
749         dst.months = Arrays.copyOf(src.months, src.months.length);
750         dst.shortMonths = Arrays.copyOf(src.shortMonths, src.shortMonths.length);
751         dst.weekdays = Arrays.copyOf(src.weekdays, src.weekdays.length);
752         dst.shortWeekdays = Arrays.copyOf(src.shortWeekdays, src.shortWeekdays.length);
753         dst.ampms = Arrays.copyOf(src.ampms, src.ampms.length);
754         if (src.zoneStrings != null) {
755             dst.zoneStrings = src.getZoneStringsImpl(true);
756         } else {
757             dst.zoneStrings = null;
758         }
759         dst.localPatternChars = src.localPatternChars;
760     }
761 
762     /**
763      * Write out the default serializable data, after ensuring the
764      * <code>zoneStrings</code> field is initialized in order to make
765      * sure the backward compatibility.
766      *
767      * @since 1.6
768      */
769     private void writeObject(ObjectOutputStream stream) throws IOException {
770         if (zoneStrings == null) {
771             zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
772         }
773         stream.defaultWriteObject();
774     }
775 
776     /**
777      * Obtains a DateFormatSymbols instance from a DateFormatSymbolsProvider
778      * implementation.
779      */
780     private static class DateFormatSymbolsGetter
781         implements LocaleServiceProviderPool.LocalizedObjectGetter<DateFormatSymbolsProvider,
782                                                                    DateFormatSymbols> {
783         private static final DateFormatSymbolsGetter INSTANCE =
784             new DateFormatSymbolsGetter();
785 
786         public DateFormatSymbols getObject(DateFormatSymbolsProvider dateFormatSymbolsProvider,
787                                 Locale locale,
788                                 String key,
789                                 Object... params) {
790             assert params.length == 0;
791             return dateFormatSymbolsProvider.getInstance(locale);
792         }
793     }
794 }